package com.forgenet.gate.socket;

import com.ecwid.consul.v1.health.model.HealthService;
import com.forgenet.gate.session.GatePlayerSessionManager;
import com.forgenet.rpc.IDispatchFacade;
import com.forgenet.message.rpc.RpcGateDispatchOtherRequest;
import com.forgenet.message.PacketId;
import com.forgenet.message.base.req.ReqGateLoginPacket;
import com.forgenet.message.base.resp.RespGateLoginPacket;
import com.sparrow.common.perf.PerfTimerManager;
import com.sparrow.common.thread.IdExecutorGroup;
import com.sparrow.common.thread.IoThreadPool;
import com.sparrow.common.util.PropertiesUtil;
import com.sparrow.common.util.TokenUtil;
import com.sparrow.event.api.EventBus;
import com.sparrow.rpc.future.RpcFuture;
import com.sparrow.rpc.service.RpcService;
import com.sparrow.rpc.util.RpcUtil;
import com.sparrow.servicemgr.consul.ConsulManager;
import com.sparrow.socket.manager.PacketHandlerManager;
import com.sparrow.socket.tcp.codec.Request;
import com.sparrow.socket.tcp.session.impl.GameSession;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

@ChannelHandler.Sharable
public class GateServerHandler extends ChannelInboundHandlerAdapter {

    private static final Logger logger = LoggerFactory.getLogger(GateServerHandler.class);

    @Autowired
    private PacketHandlerManager handlerManager;
    @Autowired
    private GatePlayerSessionManager sessionManager;
    @Autowired
    private IdExecutorGroup idExecutorGroup;
    @Autowired
    private IoThreadPool ioThreadPool;
    @Autowired
    private EventBus eventBus;
    @Autowired
    private PerfTimerManager perfTimerRegistry;
    @Autowired
    private RedissonClient redisClient;
    @Autowired
    private ConsulManager consulManager;
    @Autowired
    private RpcService rpcService;

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        GameSession session = new GameSession();
        session.setChannel(ctx.channel());
        ctx.channel().attr(GameSession.SESSION_KEY).set(session);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        GameSession session = ctx.channel().attr(GameSession.SESSION_KEY).get();
        if (session != null) {
            sessionManager.close(session);
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        GameSession session = ctx.channel().attr(GameSession.SESSION_KEY).get();
        Request request = (Request) msg;
        if (request.getMsgId() == PacketId.GateServer.REQ_GATE_LOGIN) {
            handleGateLogin(request, session);
        } else {
            dispatchToGameServer(session, request);
        }
    }

    private void handleGateLogin(Request request, GameSession session) {
        ReqGateLoginPacket reqGateLoginPacket = (ReqGateLoginPacket) request.getMsg();
        String token = reqGateLoginPacket.getToken();
        if (reqGateLoginPacket.isCreateRole()) {
            String account = TokenUtil.validateToken(token);
            if (account == null) {
                session.close();
                return;
            }
            //redis提取游戏服id
            RBucket<String> playerIdToGameData = redisClient.getBucket("account:route:" + account);
            String gameId = playerIdToGameData.get();
            HealthService gameService = consulManager.getHealthServiceByNameAndId("game", gameId);
            if (gameService == null) {
                session.close();
                return;
            }
            session.setAttribute("gameId", gameService);
            sessionManager.putUnAuthSession(account, session);
            //网关登录成功
            RespGateLoginPacket respGateLoginPacket = new RespGateLoginPacket();
            respGateLoginPacket.setCreateRole(true);
            session.writeAndFlush(respGateLoginPacket);
        } else {
            Long playerId = Long.parseLong(TokenUtil.validateToken(token));
            if (playerId == null) {
                session.close();
                return;
            }
            //redis提取游戏服id
            RBucket<String> playerIdToGameData = redisClient.getBucket("player:route:" + playerId);
            String gameId = playerIdToGameData.get();
            HealthService gameService = consulManager.getHealthServiceByNameAndId("game", gameId);
            if (gameService == null) {
                session.close();
                return;
            }
            session.setIdentity(playerId);
            session.setAttribute("gameId", gameService);
            sessionManager.bind(session, playerId);
            //网关登录成功
            session.writeAndFlush(new RespGateLoginPacket());
        }
    }

    private void dispatchToGameServer(GameSession session, Request request) {
        // 验证是否已经登录过网关（已绑定 playerId 和 gameServerId）
        RpcGateDispatchOtherRequest gateMessage = new RpcGateDispatchOtherRequest();
        gateMessage.setGateId(PropertiesUtil.getProperty("rpc.node.id"));
        gateMessage.setMsgId(request.getMsgId());
        gateMessage.setMsg(request.getMsg());
        if (session.getIdentity() != null) {
            gateMessage.setPlayerId((Long) session.getIdentity());
        }

        //那么问题来了我该选择哪个game服呢？
        //登录服账号验证成功后 分配网关和游戏服
        HealthService gameService = (HealthService) session.getAttribute("gameId");
        RpcUtil.runAsyncRpcMethod(gameService.getService().getId(), IDispatchFacade.class, service -> {
            RpcFuture<Void> f = service.dispatchToOtherReq(gateMessage);
        });
        String targetNodeId = gameService.getService().getId();
        logger.info("网关转发消息到game服务: nodeId={}, msgId={}, class={}",
                targetNodeId, request.getMsgId(), request.getMsg().getClass().getSimpleName());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //它通常出现在客户端主动关闭连接（如强制退出、断网、异常终止等）
        if (cause instanceof java.net.SocketException && cause.getMessage().contains("Connection reset")) {
            // 静默处理 Connection reset 异常，不打印堆栈
            return;
        }
        logger.error("连接异常:", cause);
        ctx.close();
    }
}
